<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>系统-服务-实例</title>

    <link rel="stylesheet" href="../../plugins/fontawesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="../../plugins/overlayScrollbars/css/OverlayScrollbars.min.css">
    <link rel="stylesheet" href="../../adminLTE/css/adminlte.min.css">
    <link rel="stylesheet" href="../css/cloud.css">

    <style>
        .table-border0 td,th {
            border: none !important;
        }
    </style>

</head>
<body class="content-wrapper" style="padding: 15px">
<!--健康状态-->
<div class="card collapsed-card">
    <div class="card-header border-0">
        <h3 class="card-title">
            健康状态
            <span id="health-total" class="text-warning ml-2">
                <i class="fa fa-check"></i> <span id="health-status">未知</span>
            </span>
        </h3>
        <div class="card-tools">
            <button type="button" class="btn btn-tool" data-card-widget="collapse">
                <i class="fa fa-plus"></i>
            </button>
        </div>
    </div>
    <div class="card-body">
        <table class="table table-hover table-sm">
            <thead><tr>
                <th style="width: 10px">#</th>
                <th>检查项</th>
                <th>状态</th>
            </tr></thead>
            <tbody id="health-data">
            <tr>
                <td>{{index}}</td>
                <td>{{name}}</td>
                <td class="{{class}}"><i class="fa {{icon}}"></i> {{status}} </td>
            </tr>
            </tbody>
        </table>
    </div>
</div>
<!--线程DUMP-->
<div class="card collapsed-card">
    <div class="card-header border-0">
        <h3 class="card-title">
            线程DUMP
            <span class="text-success ml-2">
                <i class="fa fa-check"></i> <span id="thread-dump-running">运行 0</span>
            </span>
            <span class="text-warning ml-2">
                <i class="fa fa-check"></i> <span id="thread-dump-waiting">等待 0</span>
            </span>
        </h3>
        <div class="card-tools">
            <button type="button" class="btn btn-tool" data-card-widget="collapse">
                <i class="fa fa-plus"></i>
            </button>
        </div>
    </div>
    <div class="card-body" style="height: 295px; overflow-y: auto;">
        <table id="thread-dump-data" class="table table-sm">
            <thead><tr>
                <th style="width: 10px">ID</th>
                <th style="max-width: 10em">线程</th>
            </tr></thead>
            <tbody>
            <tr>
            </tr>
            </tbody>
        </table>
    </div>
</div>
<div class="row">
    <!--进程-->
    <div class="col-md-5">
        <div class="card">
            <div id="box-thread" class="card-header border-0">
                <h3 class="card-title">进程</h3>
            </div>
            <div class="card-body">
                <table class="table table-sm table-border0" style="text-align: center">
                    <thead><tr>
                        <th class="font-weight-normal" style="width: 25%">进程ID</th>
                        <th class="font-weight-normal" style="width: 25%">端口号</th>
                        <th class="font-weight-normal" style="width: 25%">运行时间</th>
                        <th class="font-weight-normal" style="width: 25%">CPU核心</th>
                    </tr></thead>
                    <tbody>
                    <tr>
                        <td id="box-thread-pid">0</td>
                        <td id="box-thread-port">0</td>
                        <td id="box-thread-duriation">0</td>
                        <td id="box-thread-core">0</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    <!--GC-->
    <div class="col-md-4">
        <div class="card">
            <div id="box-gc" class="card-header border-0">
                <h3 class="card-title">GC</h3>
            </div>
            <div class="card-body">
                <table class="table table-sm table-border0" style="text-align: center">
                    <thead><tr>
                        <th class="font-weight-normal" style="width: 33.33%">总计</th>
                        <th class="font-weight-normal" style="width: 33.33%">总耗时</th>
                        <th class="font-weight-normal" style="width: 33.33%">最大耗时</th>
                    </tr></thead>
                    <tbody>
                    <tr>
                        <td id="box-gc-count">0</td>
                        <td id="box-gc-total">0</td>
                        <td id="box-gc-max">0</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    <!--HTTP请求-->
    <div class="col-md-3">
        <div class="card">
            <div id="box-req" class="card-header border-0">
                <h3 class="card-title">HTTP请求</h3>
            </div>
            <div class="card-body">
                <table class="table table-sm table-border0" style="text-align: center">
                    <thead><tr>
                        <th class="font-weight-normal" style="width: 50%">数量</th>
                        <th class="font-weight-normal" style="width: 50%">耗时</th>
                    </tr></thead>
                    <tbody>
                    <tr>
                        <td id="box-req-count">0</td>
                        <td id="box-req-time">0 分钟</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>

    <!--线程-->
    <div class="col-md-6">
        <div class="card">
            <div id="box-thead" class="card-header border-0">
                <h3 class="card-title">线程（个）</h3>
            </div>
            <div class="card-body">
                <canvas id="box-thead-line" style="width: 100%;"></canvas>
            </div>
        </div>
    </div>
    <!--CPU-->
    <div class="col-md-6">
        <div class="card">
            <div id="box-cpu" class="card-header border-0">
                <h3 class="card-title">CPU（%）</h3>
            </div>
            <div class="card-body" >
                <canvas id="box-cpu-line" style="width: 100%;"></canvas>
            </div>
        </div>
    </div>
    <!--内存HEAP-->
    <div class="col-md-6">
        <div class="card">
            <div id="box-mem-heap" class="card-header border-0">
                <h3 class="card-title">内存HEAP（MB）</h3>
            </div>
            <div class="card-body">
                <canvas id="box-mem-heap-line" style="width: 100%;"></canvas>
            </div>
        </div>
    </div>
    <!--内存NON-HEAP-->
    <div class="col-md-6">
        <div class="card">
            <div id="box-mem-non-heap" class="card-header border-0">
                <h3 class="card-title">内存NON-HEAP（MB）</h3>
            </div>
            <div class="card-body">
                <canvas id="box-mem-non-heap-line" style="width: 100%;"></canvas>
            </div>
        </div>
    </div>
    <!--磁盘容量-->
    <div class="col-md-4">
        <div class="card">
            <div id="box-disk" class="card-header border-0">
                <h3 class="card-title">磁盘容量</h3>
            </div>
            <div class="card-body">
                <table class="table table-sm table-border0" style="text-align: center">
                    <thead><tr>
                        <th class="font-weight-normal" style="width: 33.33%">总共</th>
                        <th class="font-weight-normal" style="width: 33.33%">已用</th>
                        <th class="font-weight-normal" style="width: 33.33%">空闲</th>
                    </tr></thead>
                    <tbody>
                    <tr>
                        <td id="box-disk-total">0 GB</td>
                        <td id="box-disk-used">0 GB</td>
                        <td id="box-disk-free">0 GB</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    <!--RabbitMQ-->
    <div class="col-md-8">
        <div class="card">
            <div id="box-rabbitmq" class="card-header border-0">
                <h3 class="card-title">RabbitMQ</h3>
            </div>
            <div class="card-body">
                <table class="table table-sm table-border0" style="text-align: center">
                    <thead><tr>
                        <th class="font-weight-normal" style="width: 16.66%">连接</th>
                        <th class="font-weight-normal" style="width: 16.66%">信道</th>
                        <th class="font-weight-normal" style="width: 16.66%">已发布</th>
                        <th class="font-weight-normal" style="width: 16.66%">已消费</th>
                        <th class="font-weight-normal" style="width: 16.66%">已确认</th>
                        <th class="font-weight-normal" style="width: 16.66%">已拒绝</th>
                    </tr></thead>
                    <tbody>
                    <tr>
                        <td id="box-rabbitmq-connection">0</td>
                        <td id="box-rabbitmq-channel">0</td>
                        <td id="box-rabbitmq-published">0</td>
                        <td id="box-rabbitmq-consumed">0</td>
                        <td id="box-rabbitmq-acknowledged">0</td>
                        <td id="box-rabbitmq-rejected">0</td>
                    </tr>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    <!--单请求耗时-->
    <div class="col-md-4">
        <div class="card">
            <div id="box-request-average" class="card-header border-0">
                <h3 class="card-title">单请求耗时（秒）</h3>
            </div>
            <div class="card-body">
                <canvas id="box-request-average-line" style="width: 100%;"></canvas>
            </div>
        </div>
    </div>
    <!--文件描述符-->
    <div class="col-md-4">
        <div class="card">
            <div id="box-fd" class="card-header border-0">
                <h3 class="card-title">文件描述符（最大值：<span id="box-fd-max">0</span>）</h3>
            </div>
            <div class="card-body">
                <canvas id="box-fd-line" style="width: 100%;"></canvas>
            </div>
        </div>
    </div>
    <!--JDBC-->
    <div class="col-md-4">
        <div class="card">
            <div id="box-jdbc" class="card-header border-0">
                <h3 class="card-title">JDBC（最大：<span id="box-jdbc-max">0</span>，最小：<span id="box-jdbc-min">0</span>）</h3>
            </div>
            <div class="card-body">
                <canvas id="box-jdbc-line" style="width: 100%;"></canvas>
            </div>
        </div>
    </div>
</div>

<script src="../../plugins/jquery/jquery.min.js"></script>
<script src="../../plugins/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="../../plugins/chart.js/Chart.js"></script>
<script src="../../adminLTE/js/adminlte.min.js"></script>
<script src="../js/common.js"></script>
<script src="../js/child.js"></script>
<script>
    var colors = ['#3c8dbc', '#0073b7', '#00c0ef', '#65aab2', '#147990'];
    var colorSuccess = "#28a745"
    var colorDanger = "#dc3545"
    var colorWarning = "#ffc107"
    var colorInfo = "#17a2b8"

    var params = Utils.getParams(location.href);
    var service = params["service"];
    var instance = params["instance"];

    function InstanceControl() {
        this.actuators = {};
        this.metrics = [];

        this.threadLine = undefined
        this.threadLineMax = 30
        this.threadLineData = {
            labels: [],
            datasets: [
                {label: "线程峰值", pointRadius: false, borderColor: colorDanger, backgroundColor: colorDanger, data: [], fill: false},
                {label: "活动线程", pointRadius: false, borderColor: colorWarning, backgroundColor: colorSuccess, data: [], fill: false},
                {label: "守护线程", pointRadius: false, borderColor: colorSuccess, backgroundColor: colorWarning, data: [], fill: false},
            ]
        }

        this.requestAverageLine = undefined
        this.requestAverageLineMax = 30
        this.requestAverageLineData = {
            labels: [],
            datasets: [
                {label: "平均", pointRadius: false, borderColor: colorSuccess, backgroundColor: colorSuccess, data: [], fill: false},
            ]
        }

        this.cpuLine = undefined
        this.cpuLineMax = 30
        this.cpuLineData = {
            labels: [],
            datasets: [
                {label: "JVM", pointRadius: false, borderColor: colorSuccess, backgroundColor: colorSuccess, data: [], fill: false},
                {label: "系统", pointRadius: false, borderColor: colorWarning, backgroundColor: colorWarning, data: [], fill: false},
            ]
        }

        this.memHeapLine = undefined
        this.memHeapLineMax = 30
        this.memHeapLineData = {
            labels: [],
            datasets: [
                {label: "最大", pointRadius: false, borderColor: colorDanger, backgroundColor: colorDanger, data: [], fill: false},
                {label: "可用", pointRadius: false, borderColor: colorWarning, backgroundColor: colorSuccess, data: [], fill: false},
                {label: "已用", pointRadius: false, borderColor: colorSuccess, backgroundColor: colorWarning, data: [], fill: false},
            ]
        }

        this.memNonHeapLine = undefined
        this.memNonHeapLineMax = 30
        this.memNonHeapLineData = {
            labels: [],
            datasets: [
                {label: "最大", pointRadius: false, borderColor: colorDanger, backgroundColor: colorDanger, data: [], fill: false},
                {label: "可用", pointRadius: false, borderColor: colorWarning, backgroundColor: colorSuccess, data: [], fill: false},
                {label: "已用", pointRadius: false, borderColor: colorSuccess, backgroundColor: colorWarning, data: [], fill: false},
            ]
        }

        this.fdLine = undefined
        this.fdLineMax = 30
        this.fdLineData = {
            labels: [],
            datasets: [
                {label: "文件描述符", pointRadius: false, borderColor: colorSuccess, backgroundColor: colorSuccess, data: [], fill: false},
            ]
        }

        this.jdbcLine = undefined
        this.jdbcLineMax = 30
        this.jdbcLineData = {
            labels: [],
            datasets: [
                {label: "活动", pointRadius: false, borderColor: colorSuccess, backgroundColor: colorSuccess, data: [], fill: false},
                {label: "空闲", pointRadius: false, borderColor: colorWarning, backgroundColor: colorWarning, data: [], fill: false},
            ]
        }

        this.healthTemplate = ""

        this.threadDumpFix = 2;
        this.threadDumpMax = 20;

        this.init = function () {
            var self = this;

            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var links = result["_links"];
                    for (let key in links) {
                        self.actuators[key] = links[key]["href"];
                    }
                }
            });

            if (this.actuators["metrics"]) {
                $.ajax({
                    url: "/api/" + service.toLowerCase() + "/actuator/metrics",
                    type: HttpMethod.get,
                    async: false,
                    success: function (result, status, xhr) {
                        self.metrics = result["names"];
                    }
                });
            }

            this.healthTemplate = $("#health-data").html();
            $("#health-data").html("");

            for (let i = 0; i < this.threadLineMax; i++) {
                this.threadLineData.labels.push(i + 1);
                this.threadLineData.datasets[0].data.push(null);
                this.threadLineData.datasets[1].data.push(null);
                this.threadLineData.datasets[2].data.push(null);
            }
            this.threadLine = new Chart($("#box-thead-line").get(0).getContext("2d"), {
                type: 'line',
                data: self.threadLineData,
                options: {scales: {xAxes: [{display: false}], yAxes: [{ticks: {min: 0, suggestedMax: 100}}]}}
            });

            for (let i = 0; i < this.requestAverageLineMax; i++) {
                this.requestAverageLineData.labels.push(i + 1);
                this.requestAverageLineData.datasets[0].data.push(null);
            }
            this.requestAverageLine = new Chart($("#box-request-average-line").get(0).getContext("2d"), {
                type: 'line',
                data: self.requestAverageLineData,
                options: {legend: false, scales: {xAxes: [{display: false}], yAxes: [{ticks: {min: 0, suggestedMax: 1}}]}}
            });

            for (let i = 0; i < this.cpuLineMax; i++) {
                this.cpuLineData.labels.push(i + 1);
                this.cpuLineData.datasets[0].data.push(null);
                this.cpuLineData.datasets[1].data.push(null);
            }
            this.cpuLine = new Chart($("#box-cpu-line").get(0).getContext("2d"), {
                type: 'line',
                data: self.cpuLineData,
                options: {scales: {xAxes: [{display: false}], yAxes: [{ticks: {min: 0, suggestedMax: 0.1}}]}}
            });

            for (let i = 0; i < this.jdbcLineMax; i++) {
                this.jdbcLineData.labels.push(i + 1);
                this.jdbcLineData.datasets[0].data.push(null);
                this.jdbcLineData.datasets[1].data.push(null);
            }
            this.jdbcLine = new Chart($("#box-jdbc-line").get(0).getContext("2d"), {
                type: 'line',
                data: self.jdbcLineData,
                options: {scales: {xAxes: [{display: false}], yAxes: [{ticks: {min: 0, suggestedMax: 15}}]}}
            });

            for (let i = 0; i < this.fdLineMax; i++) {
                this.fdLineData.labels.push(i + 1);
                this.fdLineData.datasets[0].data.push(null);
            }
            this.fdLine = new Chart($("#box-fd-line").get(0).getContext("2d"), {
                type: 'line',
                data: self.fdLineData,
                options: {legend: false, scales: {xAxes: [{display: false}], yAxes: [{ticks: {min: 0, suggestedMax: 200}}]}}
            });

            for (let i = 0; i < this.memHeapLineMax; i++) {
                this.memHeapLineData.labels.push(i + 1);
                this.memHeapLineData.datasets[0].data.push(null);
                this.memHeapLineData.datasets[1].data.push(null);
                this.memHeapLineData.datasets[2].data.push(null);
            }
            this.memHeapLine = new Chart($("#box-mem-heap-line").get(0).getContext("2d"), {
                type: 'line',
                data: self.memHeapLineData,
                options: {scales: {xAxes: [{display: false}], yAxes: [{ticks: {min: 0, suggestedMax: 5120}}]}}
            });

            for (let i = 0; i < this.memNonHeapLineMax; i++) {
                this.memNonHeapLineData.labels.push(i + 1);
                this.memNonHeapLineData.datasets[0].data.push(null);
                this.memNonHeapLineData.datasets[1].data.push(null);
                this.memNonHeapLineData.datasets[2].data.push(null);
            }
            this.memNonHeapLine = new Chart($("#box-mem-non-heap-line").get(0).getContext("2d"), {
                type: 'line',
                data: self.memNonHeapLineData,
                options: {scales: {xAxes: [{display: false}], yAxes: [{ticks: {min: 0, suggestedMax: 5120}}]}}
            });
        }
        this.health = function () {
            if (!this.actuators["health"]) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/health",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    if (result.status === "UP") {
                        $("#health-total").removeClass("text-warning").addClass("text-success");
                        $("#health-status").html("UP");
                    } else if (result.status === "DOWN") {
                        $("#health-total").removeClass("text-warning").addClass("text-danger");
                        $("#health-status").html("DOWN");
                    } else {
                        $("#health-total").removeClass("text-warning").addClass("text-warning");
                        $("#health-status").html("UNKNOWN");
                    }

                    var index = 1;
                    for (const key in result.components) {
                        var component = result.components[key];
                        component.class = "";
                        component.icon = "";
                        if (component.status === "UP") {
                            component.class = "text-success";
                            component.icon = "fa-check";
                        } else if (component.status === "DOWN") {
                            component.class = "text-danger";
                            component.icon = "fa-bug";
                        } else {
                            component.class = "text-warning";
                            component.icon = "fa-question";
                        }
                        var html = self.healthTemplate
                            .replace(new RegExp("{{index}}", "gm"), index)
                            .replace(new RegExp("{{class}}", "gm"), component.class)
                            .replace(new RegExp("{{icon}}", "gm"), component.icon)
                            .replace(new RegExp("{{name}}", "gm"), key)
                            .replace(new RegExp("{{status}}", "gm"), component.status);
                        $("#health-data").append(html);

                        index++;

                        if (key === "diskSpace") {
                            var total = result.components[key]["details"]["total"];
                            var free = result.components[key]["details"]["free"];
                            var used = total - free;

                            $("#box-disk-total").html(Math.round(total / 1024 / 1024 / 1024 * 1000) / 1000 + " GB");
                            $("#box-disk-used").html(Math.round(used / 1024 / 1024 / 1024 * 1000) / 1000 + " GB");
                            $("#box-disk-free").html(Math.round(free / 1024 / 1024 / 1024 * 1000) / 1000 + " GB");

                        }
                    }
                }
            });
        }
        this.env = function () {
            if (!this.actuators["env"]) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/env",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    for (var i = 0; i < result.propertySources.length; i++) {
                        var prop = result.propertySources[i];
                        if (prop.name === "server.ports") {
                            $("#box-thread-port").html(prop["properties"]["local.server.port"]["value"]);
                        }
                        if (prop.name === "systemProperties") {
                            $("#box-thread-pid").html(prop["properties"]["PID"]["value"]);
                        }
                    }
                }
            });
        };
        this.uptime = function () {
            if (!this.metrics.includes("process.uptime")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/process.uptime",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var second = result["measurements"][0]["value"] * 1000;
                    var duriaton = "";
                    if (second < 1000) {
                        duriaton = "<1秒";
                    } else if (second < 60000) {
                        duriaton = Math.round(second / 1000) + "秒";
                    } else if (second < 3600000) {
                        duriaton = Math.round(second / 60000) + "分钟";
                    } else if (second < 86400000) {
                        duriaton = Math.round(second / 3600000) + "小时";
                    } else {
                        duriaton = Math.round(second / 86400000) + "天" + Math.round(second % 86400000 / 3600000) + "小时";
                    }
                    $("#box-thread-duriation").html(duriaton);
                }
            });
        }
        this.cpuCount = function () {
            if (!this.metrics.includes("system.cpu.count")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/system.cpu.count",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    $("#box-thread-core").html(result["measurements"][0]["value"]);
                }
            });
        }
        this.gc = function () {
            if (!this.metrics.includes("jvm.gc.pause")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.gc.pause",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var measurements = result["measurements"];
                    for (let i = 0; i < measurements.length; i++) {
                        var measurement = measurements[i];
                        if (measurement["statistic"] === "COUNT") {
                            $("#box-gc-count").html(measurement["value"]);
                        }
                        if (measurement["statistic"] === "TOTAL_TIME") {
                            $("#box-gc-total").html(Math.round(measurement["value"] * 1000) / 1000);
                        }
                        if (measurement["statistic"] === "MAX") {
                            $("#box-gc-max").html(Math.round(measurement["value"] * 1000000) / 1000000);
                        }
                    }
                }
            });
        }
        this.thread = function () {
            if (!this.metrics.includes("jvm.threads.peak")) return;
            if (!this.metrics.includes("jvm.threads.live")) return;
            if (!this.metrics.includes("jvm.threads.daemon")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.threads.peak",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    self.threadLineData.datasets[0].data.push(usage);
                    if (self.threadLineData.datasets[0].data.length > self.threadLineMax) {
                        self.threadLineData.datasets[0].data.shift();
                    }
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.threads.live",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    self.threadLineData.datasets[1].data.push(usage);
                    if (self.threadLineData.datasets[1].data.length > self.threadLineMax) {
                        self.threadLineData.datasets[1].data.shift();
                    }
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.threads.daemon",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    self.threadLineData.datasets[2].data.push(usage);
                    if (self.threadLineData.datasets[2].data.length > self.threadLineMax) {
                        self.threadLineData.datasets[2].data.shift();
                    }
                }
            });

            this.threadLine.data.datasets = this.threadLineData.datasets;
            this.threadLine.update();
        }
        this.request = function () {
            if (!this.metrics.includes("http.server.requests")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/http.server.requests",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var count = 0, totalSec = 0, totalMin = 0, max = 0;
                    var measurements = result["measurements"];
                    for (let i = 0; i < measurements.length; i++) {
                        var measurement = measurements[i];
                        if (measurement["statistic"] === "COUNT") {
                            count = measurement["value"];
                        }
                        if (measurement["statistic"] === "TOTAL_TIME") {
                            totalMin = measurement["value"];
                            totalSec = measurement["value"];
                            totalSec = totalSec * 60;
                        }
                        if (measurement["statistic"] === "MAX") {
                            max = measurement["value"];
                        }
                    }
                    var avg = totalSec / count;

                    self.requestAverageLineData.datasets[0].data.push(avg);
                    if (self.requestAverageLineData.datasets[0].data.length > self.requestAverageLineMax) {
                        self.requestAverageLineData.datasets[0].data.shift();
                    }
                    self.requestAverageLine.data.datasets = self.requestAverageLineData.datasets;
                    self.requestAverageLine.update();

                    $("#box-req-count").html(count);
                    $("#box-req-time").html(Math.round(totalMin * 1000) / 1000 + " 分钟");
                }
            });
        }
        this.cpu = function () {
            if (!this.metrics.includes("process.cpu.usage")) return;
            if (!this.metrics.includes("system.cpu.usage")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/process.cpu.usage",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    self.cpuLineData.datasets[0].data.push(usage);
                    if (self.cpuLineData.datasets[0].data.length > self.cpuLineMax) {
                        self.cpuLineData.datasets[0].data.shift();
                    }
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/system.cpu.usage",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    self.cpuLineData.datasets[1].data.push(usage);
                    if (self.cpuLineData.datasets[1].data.length > self.cpuLineMax) {
                        self.cpuLineData.datasets[1].data.shift();
                    }
                }
            });

            this.cpuLine.data.datasets = this.cpuLineData.datasets;
            this.cpuLine.update();
        }
        this.memHeap = function () {
            if (!this.metrics.includes("jvm.memory.max")) return;
            if (!this.metrics.includes("jvm.memory.committed")) return;
            if (!this.metrics.includes("jvm.memory.used")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.memory.max?tag=area:heap",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    usage = usage / 1024 / 1024;
                    self.memHeapLineData.datasets[0].data.push(usage);
                    if (self.memHeapLineData.datasets[0].data.length > self.memHeapLineMax) {
                        self.memHeapLineData.datasets[0].data.shift();
                    }
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.memory.committed?tag=area:heap",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    usage = usage / 1024 / 1024;
                    self.memHeapLineData.datasets[1].data.push(usage);
                    if (self.memHeapLineData.datasets[1].data.length > self.memHeapLineMax) {
                        self.memHeapLineData.datasets[1].data.shift();
                    }
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.memory.used?tag=area:heap",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    usage = usage / 1024 / 1024;
                    self.memHeapLineData.datasets[2].data.push(usage);
                    if (self.memHeapLineData.datasets[2].data.length > self.memHeapLineMax) {
                        self.memHeapLineData.datasets[2].data.shift();
                    }
                }
            });

            this.memHeapLine.data.datasets = this.memHeapLineData.datasets;
            this.memHeapLine.update();
        }
        this.memNonHeap = function () {
            if (!this.metrics.includes("jvm.memory.max")) return;
            if (!this.metrics.includes("jvm.memory.committed")) return;
            if (!this.metrics.includes("jvm.memory.used")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.memory.max?tag=area:nonheap",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    usage = usage / 1024 / 1024;
                    self.memNonHeapLineData.datasets[0].data.push(usage);
                    if (self.memNonHeapLineData.datasets[0].data.length > self.memNonHeapLineMax) {
                        self.memNonHeapLineData.datasets[0].data.shift();
                    }
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.memory.committed?tag=area:nonheap",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    usage = usage / 1024 / 1024;
                    self.memNonHeapLineData.datasets[1].data.push(usage);
                    if (self.memNonHeapLineData.datasets[1].data.length > self.memNonHeapLineMax) {
                        self.memNonHeapLineData.datasets[1].data.shift();
                    }
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jvm.memory.used?tag=area:nonheap",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    usage = usage / 1024 / 1024;
                    self.memNonHeapLineData.datasets[2].data.push(usage);
                    if (self.memNonHeapLineData.datasets[2].data.length > self.memNonHeapLineMax) {
                        self.memNonHeapLineData.datasets[2].data.shift();
                    }
                }
            });

            this.memNonHeapLine.data.datasets = this.memNonHeapLineData.datasets;
            this.memNonHeapLine.update();
        }
        this.fdMax = function () {
            if (!this.metrics.includes("process.files.max")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/process.files.max",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var value = result["measurements"][0]["value"];
                    $("#box-fd-max").html(value);
                }
            });
        }
        this.fd = function () {
            if (!this.metrics.includes("process.files.open")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/process.files.open",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    self.fdLineData.datasets[0].data.push(usage);
                    if (self.fdLineData.datasets[0].data.length > self.fdLineMax) {
                        self.fdLineData.datasets[0].data.shift();
                    }
                    self.fdLine.data.datasets = self.fdLineData.datasets;
                    self.fdLine.update();
                }
            });
        }
        this.rabbitmq = function () {
            if (!this.metrics.includes("rabbitmq.connections")) return;
            if (!this.metrics.includes("rabbitmq.channels")) return;
            if (!this.metrics.includes("rabbitmq.published")) return;
            if (!this.metrics.includes("rabbitmq.consumed")) return;
            if (!this.metrics.includes("rabbitmq.acknowledged")) return;
            if (!this.metrics.includes("rabbitmq.rejected")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/rabbitmq.connections",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var value = result["measurements"][0]["value"];
                    $("#box-rabbitmq-connection").html(value)
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/rabbitmq.channels",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var value = result["measurements"][0]["value"];
                    $("#box-rabbitmq-channel").html(value)
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/rabbitmq.published",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var value = result["measurements"][0]["value"];
                    $("#box-rabbitmq-published").html(value)
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/rabbitmq.consumed",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var value = result["measurements"][0]["value"];
                    $("#box-rabbitmq-consumed").html(value)
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/rabbitmq.acknowledged",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var value = result["measurements"][0]["value"];
                    $("#box-rabbitmq-acknowledged").html(value)
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/rabbitmq.rejected",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var value = result["measurements"][0]["value"];
                    $("#box-rabbitmq-rejected").html(value)
                }
            });
        }
        this.jdbc = function () {
            if (!this.metrics.includes("jdbc.connections.max")) return;
            if (!this.metrics.includes("jdbc.connections.min")) return;
            if (!this.metrics.includes("jdbc.connections.active")) return;
            if (!this.metrics.includes("jdbc.connections.idle")) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jdbc.connections.max",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    $("#box-jdbc-max").html(usage);
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jdbc.connections.min",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    $("#box-jdbc-min").html(usage);
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jdbc.connections.active",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    self.jdbcLineData.datasets[0].data.push(usage);
                    if (self.jdbcLineData.datasets[0].data.length > self.jdbcLineMax) {
                        self.jdbcLineData.datasets[0].data.shift();
                    }
                }
            });
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/metrics/jdbc.connections.idle",
                type: HttpMethod.get,
                async: false,
                success: function (result, status, xhr) {
                    var usage = result["measurements"][0]["value"];
                    self.jdbcLineData.datasets[1].data.push(usage);
                    if (self.jdbcLineData.datasets[1].data.length > self.jdbcLineMax) {
                        self.jdbcLineData.datasets[1].data.shift();
                    }
                }
            });
            self.jdbcLine.data.datasets = self.jdbcLineData.datasets;
            self.jdbcLine.update();
        }
        this.threadDump = function () {
            if (!this.actuators["threaddump"]) return;
            var self = this;
            $.ajax({
                url: "/api/" + service.toLowerCase() + "/actuator/threaddump",
                type: HttpMethod.get,
                success: function (result, status, xhr) {
                    var now = new Date();
                    var nowSec = now.getSeconds();
                    var nowMin = now.getMinutes();
                    var nowTime = ("00" + nowMin).slice(-2) + ":" + ("00" + nowSec).slice(-2);

                    if ($("#thread-dump-data thead tr th").length === self.threadDumpMax) {
                        $("#thread-dump-data thead tr th:nth-child(3)").remove();
                        $("#thread-dump-data tbody tr td:nth-child(3)").remove();
                    }

                    var threads = result["threads"];
                    var waitCount = 0, runCount = 0;
                    for (let i = 0; i < threads.length; i++) {
                        var id = threads[i]["threadId"];
                        var name = threads[i]["threadName"];
                        var state = threads[i]["threadState"];
                        var color = (state === "RUNNABLE" ? "bg-success" : "bg-warning");
                        waitCount = (state === "RUNNABLE" ? waitCount : waitCount + 1);
                        runCount = (state === "RUNNABLE" ? runCount + 1 : runCount);

                        var $line = $("#thread-dump-data tbody tr[id=" + id +"]");
                        if ($line.length === 0) {
                            $line = $("<tr id='" + id + "'>");
                            $line.append("<td>" + id + "</td>");
                            if (name.length > 20) {
                                $line.append("<td title='" + name + "'>" + name.slice(0, 17) + "...</td>")
                            } else {
                                $line.append("<td>" + name+ "</td>")
                            }
                            var colCnt = $("#thread-dump-data thead tr th").length;
                            for (let j = self.threadDumpFix; j < colCnt; j++) {
                               $line.append("<td class='bg-white'></td>")
                            }
                            $("#thread-dump-data tbody").append($line);
                        }

                        $line.append("<td class='" + color + "'></td>");
                    }

                    $("#thread-dump-data thead tr").append("<th>" + nowTime + "</th>");

                    $("#thread-dump-waiting").html("等待 " + waitCount);
                    $("#thread-dump-running").html("运行 " + runCount);
                }
            });
        }
    }

    var instanceControl = new InstanceControl();
    instanceControl.init();
    instanceControl.health();
    instanceControl.env();
    instanceControl.uptime()
    instanceControl.cpuCount()
    instanceControl.gc()
    instanceControl.thread();
    instanceControl.request();
    instanceControl.cpu();
    instanceControl.memHeap();
    instanceControl.memNonHeap();
    instanceControl.fdMax();
    instanceControl.fd();
    instanceControl.rabbitmq();
    instanceControl.jdbc();
    instanceControl.threadDump();

    setInterval(function () {
        instanceControl.gc();
        instanceControl.thread();
        instanceControl.request();
        instanceControl.cpu();
        instanceControl.memHeap();
        instanceControl.memNonHeap();
        instanceControl.fd();
        instanceControl.rabbitmq();
        instanceControl.jdbc();
        instanceControl.threadDump();
    }, 1000)


</script>

</body>
</html>